package puf

import chisel3._
import chisel3.util._
// import chisel3.experimental._

import puf._ 
import puf.PUF_Constants._

class PUF_Core2  extends Module {
    val io = IO( new Bundle{
        val out =   new PUF_Core_Output_Ports
        val in  =   new PUF_Core_Input_Ports
        val clk =   new PUF_Core_Input_clk
    } )

    val the_ena_in      =   Wire(Bool())
    val the_ena_out     =   Reg(Bool())
    val the_clk_in      =   Wire(Bool())
    val the_clk_out     =   Wire(Bool())
    val the_data_in     =   Wire(UInt(1.W))

    io.out.Data_in      :=  the_data_in
    io.out.Ena_in       :=  the_ena_in
    io.out.Ena_out      :=  RegNext(the_ena_out)
    io.out.Clk_in       :=  the_clk_in
    io.out.Clk_out      :=  the_clk_out  

    val count_clk_in    =   WireInit(0.U(WIDTH_CNT.W))
    val count_clk_delay =   WireInit(0.U(WIDTH_CNT.W))
    val count_clk_enable=   WireInit(0.U(WIDTH_CNT.W))
    val count_clk_out   =   WireInit(0.U(WIDTH_CNT.W))
    
    val trigger_input   =   Wire(Bool())
    val trigger_delay   =   Wire(Bool())
    val trigger_enable  =   Wire(Bool())
    val trigger_output  =   Wire(Bool())
    val trigger_idle    =   Wire(Bool())

    trigger_input       :=  io.in.Start
    trigger_delay       :=  ( count_clk_in === WIDTH_DATA.U )
    trigger_enable      :=  ( count_clk_delay === DELAY_CNT.U )
    trigger_output      :=  ( count_clk_enable === ENABLE_CNT.U )
    trigger_idle        :=  ( count_clk_out === TRANS_CNT.U - ENABLE_CNT.U )
    
    
    // --- FSM ----
    val stateReg    =   RegInit(s2IDLE)

    val M_Count_Clk         =   Module(new Count_Clk2)
    M_Count_Clk.clock       :=  io.clk.Clk
    M_Count_Clk.io.Data     :=  io.in.Data
    M_Count_Clk.io.stateReg :=  stateReg
    count_clk_in            :=  M_Count_Clk.io.count_clk_in
    count_clk_delay         :=  M_Count_Clk.io.count_clk_delay
    count_clk_enable        :=  M_Count_Clk.io.count_clk_enable
    count_clk_out           :=  M_Count_Clk.io.count_clk_out
    the_data_in             :=  M_Count_Clk.io.Data_in

    when( stateReg === s2INPUT ){
        the_ena_in      :=  false.B
        the_ena_out     :=  false.B
        the_clk_in      :=  true.B
        the_clk_out     :=  false.B
    }
    .elsewhen( stateReg === s2DELAY ){
        the_ena_in      :=  true.B
        the_ena_out     :=  false.B
        the_clk_in      :=  false.B
        the_clk_out     :=  false.B
    }
    .elsewhen( stateReg === s2ENABLE ){
        the_ena_in      :=  true.B
        the_ena_out     :=  true.B
        the_clk_in      :=  false.B
        the_clk_out     :=  true.B
    }
    .elsewhen( stateReg === s2OUTPUT ){
        the_ena_in      :=  false.B
        the_ena_out     :=  true.B
        the_clk_in      :=  true.B // false.B
        the_clk_out     :=  true.B
    }
    .otherwise{
        the_ena_in      :=  false.B
        the_ena_out     :=  false.B
        the_clk_in      :=  false.B
        the_clk_out     :=  false.B
    }


    when( stateReg === s2INPUT ){
        when( trigger_delay ) {   //  计数
            stateReg    :=  s2DELAY
        }
    }
    .elsewhen( stateReg === s2DELAY ){
        when( trigger_enable ){
            stateReg    :=  s2ENABLE
        }
    }
    .elsewhen( stateReg === s2ENABLE){
        when( trigger_output ) {
            stateReg    :=  s2OUTPUT
        }
    }
    .elsewhen( stateReg === s2OUTPUT){
        when( trigger_idle ) {
            stateReg    :=  s2IDLE
        }
    }
    .otherwise {
        when( trigger_input ) {
            stateReg    :=  s2INPUT
        }
    }

}

class Count_Clk2 extends Module{
    val io = IO(new Bundle{
        val Data_in             =   Output(UInt(1.W))
        val count_clk_in        =   Output(UInt(WIDTH_CNT.W))
        val count_clk_delay     =   Output(UInt(WIDTH_CNT.W))
        val count_clk_enable    =   Output(UInt(WIDTH_CNT.W))
        val count_clk_out       =   Output(UInt(WIDTH_CNT.W))

        val Data                =   Input(UInt(WIDTH_DATA.W))
        val stateReg            =   Input(UInt(3.W))
    })
    dontTouch(io)
    val count_clk_in    =   RegInit(0.U(WIDTH_CNT.W))
    val count_clk_delay =   RegInit(0.U(WIDTH_CNT.W))
    val count_clk_enable=   RegInit(0.U(WIDTH_CNT.W))
    val count_clk_out   =   RegInit(0.U(WIDTH_CNT.W))
    val the_data_in     =   Wire(UInt(1.W))

    the_data_in     :=  ( io.Data >> count_clk_in )(0)

    when( io.stateReg === s2INPUT ){
        count_clk_in    :=  count_clk_in + 1.U 
    }.otherwise{
        count_clk_in    :=  0.U 
    }

    when( io.stateReg === s2DELAY ){
        count_clk_delay :=  count_clk_delay + 1.U 
    }
    .otherwise{
        count_clk_delay := 0.U 
    }
    
    when( io.stateReg === s2ENABLE ){
        count_clk_enable    :=  count_clk_enable + 1.U 
    }
    .otherwise{
        count_clk_enable    :=  0.U
    }

    when( io.stateReg === s2OUTPUT ){
        count_clk_out   :=  count_clk_out + 1.U 
    }
    .otherwise{
        count_clk_out   :=  0.U
    }

    io.Data_in          :=  the_data_in
    io.count_clk_in     :=  count_clk_in
    io.count_clk_delay  :=  count_clk_delay
    io.count_clk_enable :=  count_clk_enable
    io.count_clk_out    :=  count_clk_out
}